package jass.generators;

/** Second order Butterworth lowpass filter.
    @author Kees van den Doel (kvdoel@cs.ubc.ca)
*/

public class Butter2LowFilter implements Filter {
        
    /** Sampling rate in Hertz. */
    protected float srate;
    
    /** State of filter. */
    private float yt_1, yt_2,xt_1,xt_2;

    /** Cutoff frequency in Hertz. */
    protected float f;
    
    /** Coefficients (Steiglitz notation). */
    protected float cc, dd;

    /** Gain. A0 is normalized. A is actual gain, gain is user settable*/
    protected float A = 1.f, A0=1.f, gain=1;

    /** Create and initialize.
        @param srate sampling rate in Hertz.
     */
    public Butter2LowFilter(float srate) {
        this.srate = srate;
        reset();
    }

    /** Set the filter cutoff.
        @param f cutoff frequency in Hertz.
     */
    public synchronized void setCutoffFrequency(float f) {
        float tt = (float)(Math.sqrt(2)/Math.tan(2*Math.PI*f/srate));
        float tp = tt + 1, tm = tt - 1;
        float den = tp*tp+1;
        float re = (tm*tp-1)/den;
        float im = 2*tt/den;
        cc = -2*re;
        dd = re*re+im*im;
        A0 = (1 + cc + dd)/4;
        A = gain*A0;
    }

    /** Set gain.
        @param gain gain.
    */
    public synchronized void setGain(float gain) {
        A = gain*A0;
        this.gain = gain;
    }
    
    /** Reset state.
     */
    public synchronized void reset() {
        yt_1 = yt_2 = xt_1 = xt_2 = 0;
    }

        
    /** Reset state.
        @param c value of constant state
     */
    public synchronized void reset(float c) {
        yt_1 = yt_2 = xt_1 = xt_2 = c;
    }

    /** Proces input (may be same as output).
        @param output user provided buffer for returned result.
        @param input user provided input buffer.
        @param nsamples number of samples written to output buffer.
        @param inputOffset where to start in circular buffer input.
    */
    public synchronized void filter(float [] output, float[] input, int nsamples, int inputOffset) {
        if(inputOffset == 0) {
            for(int k=0;k<nsamples;k++) {
                float ynew = input[k] + 2*xt_1 + xt_2 - cc*yt_1 - dd*yt_2;
                yt_2 = yt_1;
                yt_1 = ynew;
                xt_2 = xt_1;
                xt_1 = input[k];
                output[k] = A*ynew;
            }
        } else {
            int inputLen = input.length;
            int ii = inputOffset;
            for(int k=0;k<nsamples;k++) {
                float ynew = input[ii] + 2*xt_1 + xt_2 - cc*yt_1 - dd*yt_2;
                yt_2 = yt_1;
                yt_1 = ynew;
                xt_2 = xt_1;
                xt_1 = input[ii];
                output[k] = A*ynew;
                if(ii == inputLen - 1) {
                    ii = 0;
                } else {
                    ii++;
                }
            }
        }
    }

}
